{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "LeNet\n",
    "卷积神经网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import d2lzh as d2l\n",
    "import mxnet as mx\n",
    "from mxnet import autograd, gluon, init, nd\n",
    "from mxnet.gluon import loss as gloss, nn\n",
    "import time\n",
    "\n",
    "net = nn.Sequential()\n",
    "net.add(nn.Conv2D(channels=6, kernel_size=5, activation='sigmoid'),\n",
    "        nn.MaxPool2D(pool_size=2, strides=2),\n",
    "        nn.Conv2D(channels=6, kernel_size=5, activation='sigmoid'),\n",
    "        nn.MaxPool2D(pool_size=2, strides=2),\n",
    "        # Dense会默认将(批量大小, 通道, 高, 宽)形状的输入转换成\n",
    "        # (批量大小, 通道 * 高 * 宽)形状的输入\n",
    "        nn.Dense(256, activation='relu'),\n",
    "        nn.Dense(120, activation='relu'),\n",
    "        nn.Dense(84, activation='relu'),\n",
    "        nn.Dense(10))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "conv0 output shape:\t (1, 6, 24, 24)\n",
      "pool0 output shape:\t (1, 6, 12, 12)\n",
      "conv1 output shape:\t (1, 6, 8, 8)\n",
      "pool1 output shape:\t (1, 6, 4, 4)\n",
      "dense0 output shape:\t (1, 256)\n",
      "dense1 output shape:\t (1, 120)\n",
      "dense2 output shape:\t (1, 84)\n",
      "dense3 output shape:\t (1, 10)\n"
     ]
    }
   ],
   "source": [
    "X = nd.random.uniform(shape=(1, 1, 28, 28))\n",
    "net.initialize()\n",
    "for layer in net:\n",
    "    X = layer(X)\n",
    "    print(layer.name, 'output shape:\\t', X.shape)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "batch_size = 256\n",
    "train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "cpu(0)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def try_gpu():\n",
    "    try:\n",
    "        ctx = mx.gpu()\n",
    "        _ = nd.zeros((1,), ctx=ctx)\n",
    "    except mx.base.MXNetError:\n",
    "        ctx = mx.cpu()\n",
    "    return ctx\n",
    "\n",
    "ctx = try_gpu()\n",
    "ctx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate_accuracy(data_iter, net, ctx):\n",
    "    acc_sum, n = nd.array([0], ctx=ctx), 0\n",
    "    for X, y in data_iter:\n",
    "        X, y = X.as_in_context(ctx), y.as_in_context(ctx).astype('float32')\n",
    "        acc_sum += (net(X).argmax(axis=1) == y).sum()\n",
    "        n += y.size\n",
    "    return acc_sum.asscalar() / n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 本函数已保存在d2lzh包中方便以后使用\n",
    "def train_ch5(net, train_iter, test_iter, batch_size, trainer, ctx,\n",
    "              num_epochs):\n",
    "    print('training on', ctx)\n",
    "    loss = gloss.SoftmaxCrossEntropyLoss()\n",
    "    for epoch in range(num_epochs):\n",
    "        train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()\n",
    "        for X, y in train_iter:\n",
    "            X, y = X.as_in_context(ctx), y.as_in_context(ctx)\n",
    "            with autograd.record():\n",
    "                y_hat = net(X)\n",
    "                l = loss(y_hat, y).sum()\n",
    "            l.backward()\n",
    "            trainer.step(batch_size)\n",
    "            y = y.astype('float32')\n",
    "            train_l_sum += l.asscalar()\n",
    "            train_acc_sum += (y_hat.argmax(axis=1) == y).sum().asscalar()\n",
    "            n += y.size\n",
    "        test_acc = evaluate_accuracy(test_iter, net, ctx)\n",
    "        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, '\n",
    "              'time %.1f sec'\n",
    "              % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc,\n",
    "                 time.time() - start))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "training on cpu(0)\n",
      "epoch 1, loss 1.6159, train acc 0.378, test acc 0.683, time 16.8 sec\n",
      "epoch 2, loss 0.7836, train acc 0.697, test acc 0.763, time 16.0 sec\n",
      "epoch 3, loss 0.6307, train acc 0.756, test acc 0.786, time 15.9 sec\n",
      "epoch 4, loss 0.5699, train acc 0.780, test acc 0.804, time 15.9 sec\n",
      "epoch 5, loss 0.5242, train acc 0.799, test acc 0.820, time 15.9 sec\n"
     ]
    }
   ],
   "source": [
    "lr, num_epochs = 0.5, 5\n",
    "net.initialize(force_reinit=True, ctx=ctx, init=init.Xavier())\n",
    "trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': lr})\n",
    "train_ch5(net, train_iter, test_iter, batch_size, trainer, ctx, num_epochs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
